#include "deploy/m_log_deploy.h"
#include "m_log.h"
#include "m_srt.h"
#include "m_public.h"
#include "m_memory.h"
#include "m_mutex.h"
#include "m_list.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>

typedef struct MLogNodeTag
{
    UInt16 log_len;
    UInt8  log[0];
} MLogNode;

typedef struct MLogObjTag
{
    MMutex        lock_;  // 锁
    MList         list_;
    void*         print_user_;
    MLogPrintFunc print_callback_;
    MLogMode      log_mode_;
    UInt8         print_buf_[4096+128];
} MLogObj;

static Bool    is_init_ = False;  // 是否已初始化
static MLogObj m_log_;

static const Int8 log_level_color_arr_[] = {
    36,  // S 青色
    34,  // A 蓝色
    31,  // E 红色
    33,  // W 黄色
    32,  // I 绿色
    32,  // D 绿色
    37,  // T 白色
};


static const Int8* log_level_str_arr_[] = {
    "S",
    "A",
    "E",
    "W",
    "I",
    "D",
    "T",
};

static const Int8 GeMLogLevelColor(UInt8 level)
{
    return log_level_color_arr_[level];
}

static const Int8 *GeMLogLevelStr(UInt8 level)
{
    return log_level_str_arr_[level];
}

static void DoPrintLog(MLogObj* self, UInt8* log, UInt16 log_len)
{
    switch (m_log_.log_mode_)
    {
    case kMLogModeSync:
    {
        if (m_log_.print_callback_ != NULL)
            m_log_.print_callback_(m_log_.print_user_, log, log_len);
    }
        break;
    case kMLogModeAsync:
    {
        MLogNode* new_log = (MLogNode*)MMalloc(sizeof(MLogNode) + log_len);

        if (new_log != NULL)
        {
            new_log->log_len = log_len;
            memcpy(new_log->log, log, log_len);
            MListPush(&m_log_.list_, new_log);
        }
    }
        break;

    default:
        break;
    }
}

void MLog(UInt8 level, const Int8 *tag,
          const Int8 *file_name, const Int8 *func_name, Int32 line,
          const Int8 *format, ...)
{
    Bool is_log_enable = False;

    TSrtGetBool(kSrtBoolLogEnable, &is_log_enable);

    if (is_init_ && is_log_enable)
    {
        if (MMutexEnter(&m_log_.lock_))
        {
            va_list args;
            Int32 tag_len = 0;
            Int32 content_len = 0;

            mutex_enter_blocking(&m_log_.lock_);

            tag_len = snprintf(m_log_.print_buf_, sizeof(m_log_.print_buf_),
                            "\033[%dm%09u [%s <%s> %s:%s:%d] ", GeMLogLevelColor(level), MGetTicks(),
                            GeMLogLevelStr(level), tag, file_name, func_name, line);
            va_start(args, format);
            content_len = vsnprintf(&m_log_.print_buf_[tag_len], sizeof(m_log_.print_buf_) - tag_len, format, args);
            va_end(args);
            if (content_len > sizeof(m_log_.print_buf_) - tag_len - 7)
                content_len = sizeof(m_log_.print_buf_) - tag_len - 7;

            memcpy(&m_log_.print_buf_[tag_len + content_len], "\033[37m\r\n", 7);

            DoPrintLog(self, m_log_.print_buf_, tag_len + content_len + 7);

            MMutexLeave(&m_log_.lock_);
        }
    }
}

void MLogHex(UInt8 level, const Int8 *tag,
             const Int8 *file_name, const Int8 *func_name, Int32 line,
             const UInt8 *buf_tag,
             const UInt8 *buf, Int16 buf_size)
{
    Bool is_log_enable = False;

    TSrtGetBool(kSrtBoolLogEnable, &is_log_enable);

    if (is_init_ && is_log_enable)
    {
        if (MMutexEnter(&m_log_.lock_))
        {
            Int32 tag_len = 0;
            Int32 content_len = 0;

            tag_len = snprintf(m_log_.print_buf_, sizeof(m_log_.print_buf_),
                            "\033[%dm%09u [%s <%s> %s:%s:%d] %s[%d]: ", GeMLogLevelColor(level), MGetTicks(),
                            GeMLogLevelStr(level), tag, file_name, func_name, line, buf_tag, buf_size);

            content_len = MHex2Bin(&m_log_.print_buf_[tag_len], sizeof(m_log_.print_buf_) - tag_len, buf, buf_size);

            if (content_len > sizeof(m_log_.print_buf_) - tag_len - 7)
                content_len = sizeof(m_log_.print_buf_) - tag_len - 7;

            memcpy(&m_log_.print_buf_[tag_len + content_len], "\033[37m\r\n", 7);

            DoPrintLog(self, m_log_.print_buf_, tag_len + content_len + 7);

            MMutexLeave(&m_log_.lock_);
        }
    }
}

void MLogStr(UInt8 level, const Int8 *tag,
             const Int8 *file_name, const Int8 *func_name, Int32 line,
             const UInt8 *buf_tag,
             const UInt8 *buf, Int16 buf_size)
{
    Bool is_log_enable = False;

    MSrtGetBool(kSrtBoolLogEnable, &is_log_enable);

    if (is_init_ && is_log_enable)
    {
        if (MMutexEnter(&m_log_.lock_))
        {
            Int32 tag_len = 0;
            Int32 content_len = 0;

            tag_len = snprintf(m_log_.print_buf_, sizeof(m_log_.print_buf_),
                            "\033[%dm%09u [%s <%s> %s:%s:%d] %s[%d]: ", GeMLogLevelColor(level), MGetTicks(),
                            GeMLogLevelStr(level), tag, file_name, func_name, buf_tag, buf_size);

            if (sizeof(m_log_.print_buf_) - tag_len < buf_size - 7)
                content_len = sizeof(m_log_.print_buf_) - tag_len - 7;
            else
                content_len = buf_size;

            memcpy(&m_log_.print_buf_[tag_len], buf, content_len);

            memcpy(&m_log_.print_buf_[tag_len + content_len], "\033[37m\r\n", 7);

            DoPrintLog(self, m_log_.print_buf_, tag_len + content_len + 7);

            MMutexLeave(&m_log_.lock_);
        }
    }
}

Int32 MLogServerInit(void* user, MLogPrintFunc print_func, MLogMode mode)
{
    if (! is_init_)
    {
        if (print_func == NULL
             || (mode != kMLogModeSync && mode != kMLogModeAsync))
        {
            return kErrCodeParamsErr;
        }

        m_log_.print_callback_ = print_func;
        m_log_.print_user_ = user;
        m_log_.log_mode_ = mode;

        MMutexCreate(&m_log_.lock_);
        MListCreate(&m_log_.list_);

        is_init_ = True;
    }

    return kErrCodeSucc;
}

Int32 MLogServerFini(void)
{
    if (is_init_)
    {
        is_init_ = False;

        MListDestroy(&m_log_.list_);
        MMutexDestory(&m_log_.lock_);
    }

    return kErrCodeSucc;
}

void MLogServerRunOnce(UInt32 cur_ticks)
{
    if (is_init_)
    {
        if (MMutexEnter(&m_log_.lock_))
        {
            for (Int32 print_cnt = 0; print_cnt < 10; print_cnt ++)  // 一个调度最多取十条
            {
                MLogNode* print_buf = MListPop(&m_log_.list_);

                if (print_buf != NULL)
                {
                    m_log_.print_callback_(m_log_.print_user_, print_buf->log, print_buf->log_len);
                    MFree(print_buf);
                }
                else
                {
                    break;
                }
            }

            MMutexLeave(&m_log_.lock_);
        }
    }
}
